/* WindowEventHandler class */
#ifndef WINDOWEVENTHANDLER_H
#define WINDOWEVENTHANDLER_H

#include "OGRE/Ogre.h" 

#include "OIS/OIS.h" 

class WindowEventHandler : public OIS::KeyListener, public OIS::MouseListener , 
	public Ogre::WindowEventListener {
public:
	WindowEventHandler(Ogre::RenderWindow* mWindow_
		, void *wrc
		, void *wcc
		, void *wfcc
		, void *kpec
		, void *krec
		, void *mmc
		, void *mpc
		, void *mrc)
		: mWindow(mWindow_) {
		mKeyboard = 0;
		mMouse = 0;

		windowResizedCallback = (void (*)(int newwidth, int newheight))wrc;
		windowClosedCallback = (void (*)())wcc;
		windowFocusChangeCallback = (void (*)())wfcc;
		keyPressedEventCallback = (void (*)(void *keyeventobj))kpec;
		keyReleasedEventCallback = (void (*)(void *keyeventobj))krec;
		mouseMovedCallback = (void (*)(void *mouseeventobj))mmc;
		mousePressedCallback= (void (*)(void *mouseeventobj, int buttonId))mpc;
		mouseReleasedCallback = (void (*)(void *mouseeventobj, int buttonId))mrc;

		Ogre::WindowEventUtilities::addWindowEventListener(mWindow, this);
	}

	/*-----------------------------------------------------------------------------
	| Sets up OIS input.
	-----------------------------------------------------------------------------*/
	virtual void setupInput()
	{
		OIS::ParamList pl;
		size_t winHandle = 0;
		std::ostringstream winHandleStr;

		mWindow->getCustomAttribute("WINDOW", &winHandle);
		winHandleStr << winHandle;

		pl.insert(std::make_pair("WINDOW", winHandleStr.str()));

		mInputMgr = OIS::InputManager::createInputSystem(pl);

		createInputDevices();      // create the specific input devices

		windowResized(mWindow);    // do an initial adjustment of mouse area
	}

	/*-----------------------------------------------------------------------------
	| Creates the individual input devices. I only create a keyboard and mouse
	| here because they are the most common, but you can override this method
	| for other modes and devices.
	-----------------------------------------------------------------------------*/
	virtual void createInputDevices()
	{
		mKeyboard = static_cast<OIS::Keyboard*>(mInputMgr->createInputObject(OIS::OISKeyboard, true));
		// we use Unicode for character input!
		mKeyboard->setTextTranslation(OIS::Keyboard::TextTranslationMode::Unicode);
		mMouse = static_cast<OIS::Mouse*>(mInputMgr->createInputObject(OIS::OISMouse, true));

		mKeyboard->setEventCallback(this);

		mMouse->setEventCallback(this);
	}

	/*-----------------------------------------------------------------------------
	| Processes window size change event. Adjusts mouse's region to match that
	| of the window.
	-----------------------------------------------------------------------------*/
	virtual void windowResized(Ogre::RenderWindow* rw)
	{
		// manually call sample callback to ensure correct order
//		if (mCurrentSample && !mSamplePaused) mCurrentSample->windowResized(rw);

		const OIS::MouseState& ms = mMouse->getMouseState();
		ms.width = rw->getWidth();
		ms.height = rw->getHeight();

		if(windowResizedCallback) { windowResizedCallback(ms.width, ms.height); }
	}

	// window event callbacks

	virtual void windowMoved(Ogre::RenderWindow* rw)
	{

	}

	virtual bool windowClosing(Ogre::RenderWindow* rw)
	{
		return true;
	}

	virtual void windowClosed(Ogre::RenderWindow* rw)
	{

		//Only close for window that created OIS (the main window in these demos)
		if( rw == mWindow )
		{
			if(windowClosedCallback) { windowClosedCallback(); }
			shutdown();
		}
	}

	virtual void windowFocusChange(Ogre::RenderWindow* rw)
	{
		//		if (mCurrentSample && !mSamplePaused) mCurrentSample->windowFocusChange(rw);
		if(windowFocusChangeCallback) { windowFocusChangeCallback(); }
	}

	virtual bool keyPressed(const OIS::KeyEvent& evt)
	{
		//		if (mCurrentSample && !mSamplePaused) return mCurrentSample->keyPressed(evt);
		if(keyPressedEventCallback) { keyPressedEventCallback((void *)&evt); }
		return true;
	}

	virtual bool keyReleased(const OIS::KeyEvent& evt)
	{
		//		if (mCurrentSample && !mSamplePaused) return mCurrentSample->keyReleased(evt);
		if(keyReleasedEventCallback) { keyReleasedEventCallback((void *)&evt); }
		return true;
	}

	virtual bool mouseMoved(const OIS::MouseEvent& evt)
	{
		//            if (mCurrentSample && !mSamplePaused) return mCurrentSample->mouseMoved(evt);
		if(mouseMovedCallback) { mouseMovedCallback((void *)&evt); }
		return true;
	}

	virtual bool mousePressed(const OIS::MouseEvent& evt, OIS::MouseButtonID id)
	{
		//			if (mCurrentSample && !mSamplePaused) return mCurrentSample->mousePressed(evt, id);
		if(mousePressedCallback) { mousePressedCallback((void *)&evt,id); }
		return true;
	}

	virtual bool mouseReleased(const OIS::MouseEvent& evt, OIS::MouseButtonID id)
	{
		//			if (mCurrentSample && !mSamplePaused) return mCurrentSample->mouseReleased(evt, id);
		if(mouseReleasedCallback) { mouseReleasedCallback((void *)&evt,id); }
		return true;
	}

	/*-----------------------------------------------------------------------------
	| Captures input device states.
	-----------------------------------------------------------------------------*/
	virtual void captureInputDevices()
	{
		mKeyboard->capture();
		mMouse->capture();
	}

	/*-----------------------------------------------------------------------------
	| Cleans up and shuts down the context.
	-----------------------------------------------------------------------------*/
	virtual void shutdown()
	{
		// remove window event listener before shutting down OIS
		Ogre::WindowEventUtilities::removeWindowEventListener(mWindow, this);

		shutdownInput();
	}

	/*-----------------------------------------------------------------------------
	| Destroys OIS input devices and the input manager.
	-----------------------------------------------------------------------------*/
	virtual void shutdownInput()
	{
		if (mInputMgr)
		{
			mInputMgr->destroyInputObject(mKeyboard);
			mInputMgr->destroyInputObject(mMouse);

			OIS::InputManager::destroyInputSystem(mInputMgr);
			mInputMgr = 0;
		}
	} 

private:

	Ogre::RenderWindow* mWindow;    // render window
	OIS::InputManager* mInputMgr;   // OIS input manager

	OIS::Keyboard* mKeyboard;       // keyboard device
	OIS::Mouse* mMouse;             // mouse device 

	void (* windowResizedCallback)(int newwidth, int newheight);
	void (* windowClosedCallback)();
	void (* windowFocusChangeCallback)();
	void (* keyPressedEventCallback)(void *keyeventobj);
	void (* keyReleasedEventCallback)(void *keyeventobj);
	void (* mouseMovedCallback)(void *mouseeventobj);
	void (* mousePressedCallback)(void *mouseeventobj, int buttonId);
	void (* mouseReleasedCallback)(void *mouseeventobj, int buttonId);

};

#endif
